package com.katze.boot.plugins.redis.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.katze.boot.common.domain.GlobalConstant;
import com.katze.boot.plugins.redis.operations.RedisHashOperations;
import com.katze.boot.plugins.redis.operations.RedisOperations;
import com.katze.boot.plugins.redis.operations.RedisValueOperations;
import com.katze.boot.plugins.redis.operations.RedisZSetOperations;
import com.katze.boot.plugins.redis.service.CacheService;
import com.katze.boot.plugins.redis.service.UserDetailsDao;
import com.katze.common.DatePattern;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service("cacheService")
public class CacheServiceImpl implements CacheService {
    private static final Logger log = LoggerFactory.getLogger(CacheServiceImpl.class);

    private final int database = -1;

    @Resource
    private ObjectMapper mapper;

    @Resource
    private RedisOperations<String> redisOperations;

    @Resource
    private RedisHashOperations<String> hashOperations;

    @Resource
    private RedisValueOperations<String> valueOperations;

    @Resource
    private RedisZSetOperations<String> zSetOperations;

    @Lazy
    @Autowired
    private UserDetailsDao<? extends UserDetails> userDetailsDao;


    @Override
    @SuppressWarnings("unchecked")
    public  <T extends UserDetails>T loadUserByUsername(String username, Class<T> cs) {
        return hashOperations.select(database, operations -> {
            String result = operations.get(GlobalConstant.USER_INFO_HASH_CACHE, username);
            try {
                if (StringUtils.isBlank(result)) {
                    return saveUser((T) userDetailsDao.loadUserDetailsByUsername(username));
                } else {
                    return this.mapper.readValue(result, cs);
                }
            } catch (Exception e) {
                log.error("{}用户信息读取失败,缓存内容: {}", username, result, e);
                return null;
            }
        });
    }

    @Override
    public <T extends UserDetails> Function<Function<String, T>, T> getUserDetails(String username, Class<T> cs) {
        String result = hashOperations.select(database, operations -> operations.get(GlobalConstant.USER_INFO_HASH_CACHE, username));
        return (databaseSelector) -> {
            try {
                if (StringUtils.isBlank(result)) {
                    return saveUser(databaseSelector.apply(username));
                } else {
                    return this.mapper.readValue(result, cs);
                }
            } catch (Exception e) {
                log.error("{}用户信息读取失败,缓存内容: {}", username, result, e);
                return null;
            }
        };
    }

    /**
     * 保存用户信息
     */
    private  <T extends UserDetails>T saveUser(T user) {
        return hashOperations.select(database, operations -> {
            try {
                if (user != null) {
                    operations.put(GlobalConstant.USER_INFO_HASH_CACHE, user.getUsername(),mapper.writeValueAsString(user));
                }
                return user;
            } catch (Exception e) {
                log.error("用户对象转JSON格式异常", e);
                return null;
            }
        });
    }


    @Override
    public void clearUserCache(String username) {
        if (StringUtils.isNotBlank(username)) {
            hashOperations.select(database, operations -> operations.delete(GlobalConstant.USER_INFO_HASH_CACHE, username));
        }
    }

    @Override
    public void saveToken(String username, String value, long timeout) {
        zSetOperations.select(database, operations -> operations.add(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, username, System.currentTimeMillis()));
        valueOperations.select(database, operations -> {
            operations.set(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(username), value, timeout, TimeUnit.MILLISECONDS);
            return value;
        });
    }

    @Override
    public String getToken(String key) {
        return valueOperations.select(database, operations -> operations.get(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(key)));
    }

    @Override
    @Deprecated
    public Boolean deleteToken(String key) {
        return redisOperations.select(database, operations -> operations.delete(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(key)));
    }

    @Override
    @Deprecated
    public Boolean saveActiveUser(String value, double score){
        return zSetOperations.select(database, operations -> operations.add(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, value, score));
    }

    @Override
    public Set<String> getAllActiveUser(){
        return zSetOperations.select(database, operations -> {
            Set<String> sets = new HashSet<>();
            try (Cursor<ZSetOperations.TypedTuple<String>> cursor = operations.scan(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, ScanOptions.NONE)){
                while (cursor.hasNext()){
                    sets.add(cursor.next().getValue());
                }
            } catch (Exception e) {
                log.error("获取在线用户失败", e);
            }
            return sets;
        });
    }

    @Override
    @Deprecated
    public Long deleteActiveUser(String value){
        return zSetOperations.select(database, operations -> operations.remove(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, value));
    }

    @Override
    public Long clearInvalidActiveUser(){
        String now = DatePattern.localDateTime(LocalDateTime.now().plusDays(-1));
        return zSetOperations.select(database, operations -> operations.removeRangeByScore(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, 0, Double.parseDouble(now)));
    }

    @Override
    public void logout(String username) {
        // 删除用户登录信息
        long count = hashOperations.select(database, operations -> operations.delete(GlobalConstant.USER_INFO_HASH_CACHE, username));
        if (count == 0) {
            log.warn("用户名:{}, {}缓存删除失败", username, GlobalConstant.USER_INFO_HASH_CACHE);
        }
        // 删除指定在线用户
        count = zSetOperations.select(database, operations -> operations.remove(GlobalConstant.ACTIVE_USERS_ZSET_CACHE, username));
        if (count == 0) {
            log.warn("用户名:{}, {}缓存删除失败", username, GlobalConstant.ACTIVE_USERS_ZSET_CACHE);
        }
        //删除用户token
        boolean status = redisOperations.select(database, operations -> operations.delete(GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX.concat(username)));
        if (!status) {
            log.warn("用户名:{}, {}缓存删除失败", username, GlobalConstant.ACCESS_TOKEN_HASH_CACHE_PREFIX);
        }
    }
}
